// serialize.cpp
//
// Copyright (C) 2008  Maksim Sipos <msipos@mailc.net>
//
// This file is a part of the Ripe language project.
//
// Ripe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "util/standard.hpp"
#include <cstring>
#include "util/serialize.hpp"

namespace Ripe {

// THE SERIALIZE FORMAT: Magic 4-bytes followed by a sequence of the structures
// of the form:
//   2-byte "type marker" which is of type uint16_t
//   n bytes of data depending on the type marker

#define SERIALIZE_MAGIC ((uint32_t) 0xdecafbad)

// TODO: One day, this serialization code should deal with endianess.

// ISerializer

ISerializer::ISerializer(std::istream* str)
{
  s = str;
  
  // Read magic signature
  uint32_t magic;
  read_expect(&magic, sizeof(magic));
  
  if (magic != SERIALIZE_MAGIC) {
    ErrorInvalidSerialize e;
    e.message = "Invalid magic signature.";
    throw e;
  }
  
  detect_future();
}

uint16_t ISerializer::detect()
{
  return detect_type;
}

uint32_t ISerializer::unserialize_uint32()
{
  expect_marker(M_UINT32);
  uint32_t i;
  read_expect(&i, sizeof(i));
  detect_future();
  return i;
}

int32_t ISerializer::unserialize_int32()
{
  expect_marker(M_INT32);
  int32_t i;
  read_expect(&i, sizeof(i));
  detect_future();
  return i;
}

uint16_t ISerializer::unserialize_uint16()
{
  expect_marker(M_UINT16);
  uint16_t i;
  read_expect(&i, sizeof(i));
  detect_future();
  return i;
}

int16_t ISerializer::unserialize_int16()
{
  expect_marker(M_INT16);
  int16_t i;
  read_expect(&i, sizeof(i));
  detect_future();
  return i;
}

char* ISerializer::unserialize_string()
{
  expect_marker(M_STRING);

  uint32_t len;
  read_expect(&len, sizeof(len));
  if (len == 0){
    throw ErrorInvalidSerialize();
  }

  str_buffer.ensure(len);
  read_expect(str_buffer.buf, len);
  detect_future();
  return str_buffer.buf;
}

void ISerializer::detect_future()
{
  uint16_t marker;
  ErrorInvalidSerialize e;
  
  s->read((char*) &marker, sizeof(marker));
  // Possibilities are:
  // - Read marker bytes OK
  // - Read nothing and hit EOF
  // - Failed

  if ( (not s->fail()) 
       and (not s->eof()) 
       and (s->gcount() == sizeof(marker)) ) {
    if (marker > M_SUPPORTED_MARKER){
      e.message.format("Invalid type %"PRIu16 " detected.", marker);
      throw e;
    }
            
    detect_type = marker;
  } else if (s->eof() and (s->gcount() == 0)) {
    detect_type = M_EOF;
  } else if (s->bad()) {
    e.message = "Failed reading the serialized binary.";
    throw e;
  } else {
    e.message = "Failed detecting serialized type.";
    throw e;
  }
}

void ISerializer::read_expect(void* buf, uint32_t buf_len)
{
  s->read((char*) buf, buf_len);
  if (s->gcount() != ((int32_t) buf_len))
    throw ErrorInvalidSerialize();
}

void ISerializer::expect_marker(uint16_t m)
{
  if (m != detect_type) {
    ErrorUnserializeType e;
    e.message.format("Expected type %"PRIu16", found %"PRIu16".", m,
                     detect_type);
    throw e;
  }
}

// OSerializer

OSerializer::OSerializer(std::ostream* str)
{
  s = str;
  uint32_t magic = SERIALIZE_MAGIC;
  s->write((const char*) &magic, sizeof(magic));
}

void OSerializer::serialize(uint32_t i)
{
  serialize_marker(M_UINT32);
  s->write((const char*) &i, sizeof(i));
}

void OSerializer::serialize(int32_t i)
{
  serialize_marker(M_INT32);
  s->write((const char*) &i, sizeof(i));
}

void OSerializer::serialize(uint16_t i)
{
  serialize_marker(M_UINT16);
  s->write((const char*) &i, sizeof(i));
}

void OSerializer::serialize(int16_t i)
{
  serialize_marker(M_INT16);
  s->write((const char*) &i, sizeof(i));
}

void OSerializer::serialize(const char* str)
{
  serialize_marker(M_STRING);
  uint32_t len = std::strlen(str) + 1;
  s->write((const char*) &len, sizeof(len));
  s->write(str, len);
}

void OSerializer::serialize_marker(uint16_t m)
{
  s->write((const char*) &m, sizeof(m));
}

}
